home *** CD-ROM | disk | FTP | other *** search
- { ** Tiny BASIC Interpreter **
-
- Author: David Benn
- Date: 21st,22nd March 1992,
- 26th-29th January 1993,
- 25th March 1993,
- 10th June 1993 }
-
- version$="$VER: TinyBASIC 1.1 10 06 1993"
-
- library exec
-
- declare function AllocMem& library exec
- declare function FreeMem library exec
-
- '..memory constants
- const MEMF_PUBLIC=1&
- const MEMF_CLEAR=65536
- const NULL=0&
-
- '..boolean constants
- const true=-1&
- const false=0&
-
- '..stack
- const maxstack=100
- dim stack(maxstack)
- shortint stacktop
-
- '..intrinsic functions
- const maxfunc=8
- dim funcs$(maxfunc)
-
- for i%=1 to maxfunc
- read funcs$(i%)
- next
-
- data "SIN","COS","TAN","LOG","SQR","FIX","INT","RND"
-
- { * tokens * }
- const maxsym=34
-
- '..special symbols
- const alpha=1
- const number=2
- const stringliteral=3
- const plus=4
- const minus=5
- const mult=6
- const div=7
- const pow=8
- const lparen=9
- const rparen=10
- const eq=11
- const lt=12
- const gt=13
- const ltoreq=14
- const gtoreq=15
- const noteq=16
- const comma=17
- const colon=18
- const eos=19
-
- '..reserved words
- const clssym=20
- const elsesym=21
- const gotosym=22
- const ifsym=23
- const inputsym=24
- const letsym=25
- const listsym=26
- const loadsym=27
- const newsym=28
- const printsym=29
- const runsym=30
- const savesym=31
- const stopsym=32
- const thensym=33
-
- const undef=maxsym
-
- '..token strings
- dim sym.name$(maxsym)
- {for i%=1 to maxsym
- read sym.name$(i%)
- next
- data alpha,number,stringliteral
- data "+","-","*","/","^"
- data "(",")","=","<",">","<=",">=","<>",",",":",eos
- data "cls","else","goto","if","input","let","list","load"
- data "new","print ","run","save","stop","then"
- data undef}
-
- '..reserved words
- const maxword=14
- dim word$(maxword)
-
- for i%=1 to maxword
- read word$(i%)
- next
-
- data "CLS","ELSE","GOTO","IF","INPUT","LET","LIST"
- data "LOAD","NEW","PRINT","RUN","SAVE","STOP","THEN"
-
- '..errors
- longint bad
- const DIVBYZERO=1
- const SYNTAX=2
- const STKOVFL=3
- const STKUFL=4
- const LINEOUTOFRANGE=5
- const NOSUCHLINE=6
- const OUTOFMEMORY=7
- const CANNOTOPENFILE=8
- const FILENOTFOUND=9
-
- '..program lines
- const maxlines=1000
- dim code_ptr&(maxlines)
-
- for i%=0 to maxlines
- code_ptr&(i%)=NULL
- next
- shortint topline
-
- '..program counter
- shortint pc,old_pc
-
- '..miscellaneous globals
- shortint n,length
- longint halt_requested
- ch$=""
- ut_ch$=""
- buf$=""
- ut_buf$=""
- obj$=""
- sym=undef
-
- '..variables
- dim var(25)
- for i%=0 to 25
- var(i%)=0
- next
-
- '..forward references
- declare SUB expr
- declare SUB statement
- declare SUB parse_line
-
- '..enable CTRL-C breaks
- ON BREAK GOTO start
- BREAK ON
-
- {SUB show.sym(n)
- shared sym.name$
- print sym.name$(n)
- END SUB}
-
- SUB er(n)
- shared bad,pc,old_pc
- if bad then exit sub '..report only 1 error per line
- case
- n=DIVBYZERO : print "DIVISION BY ZERO";
- n=SYNTAX : print "SYNTAX ERROR";
- n=STKOVFL : print "STACK OVERFLOW";
- n=STKUFL : print "STACK UNDERFLOW";
- n=LINEOUTOFRANGE : print "LINE OUT OF RANGE 1 TO";str$(maxlines);
- n=NOSUCHLINE : print "LINE DOES NOT EXIST";
- n=OUTOFMEMORY : print "OUT OF MEMORY";
- n=CANNOTOPENFILE : print "CAN'T OPEN FILE FOR WRITING";
- n=FILENOTFOUND : print "FILE NOT FOUND";
- end case
- if pc<>0 then print " IN LINE";old_pc else print
- bad=true
- END SUB
-
- SUB nextch
- shared ch$,ut_ch$,buf$,ut_buf$,n,length
-
- if n<=length then
- ch$=mid$(buf$,n,1)
- ut_ch$=mid$(ut_buf$,n,1)
- ++n
- else
- ch$=""
- end if
- END SUB
-
- SUB rsvd.wd%(x$)
- shared word$
- shortint i,num
-
- i=1
- while i<=maxword and num=0
- if x$ = word$(i) then num=i
- ++i
- wend
-
- if num=0 then rsvd.wd%=alpha else rsvd.wd%=num+eos
- END SUB
-
- SUB insymbol
- shared ch$,ut_ch$,sym,obj$
- shortint periods
-
- obj$=""
- sym=undef
-
- '...skip whitespace
- if ch$<=" " and ch$<>"" then
- repeat
- nextch
- until ch$>" " or ch$=""
- end if
-
- '..end of string?
- if ch$="" then sym=eos:exit sub
-
- '...characters
- if ch$>="A" and ch$<="Z" then
- while ch$>="A" and ch$<="Z"
- obj$=obj$+ch$
- nextch
- wend
- sym=rsvd.wd%(obj$)
- else
- '...unsigned numeric constant
- if (ch$>="0" and ch$<="9") or ch$="." then
- sym=number
- while (ch$>="0" and ch$<="9") or ch$="."
- if ch$="." then ++periods
- obj$=obj$+ch$
- nextch
- wend
- if periods > 1 then
- sym=undef
- er(SYNTAX)
- end if
- else
- '..string literal
- if ch$=chr$(34) then
- sym=stringliteral
- nextch
- while ch$<>chr$(34) and ch$<>""
- obj$=obj$+ut_ch$
- nextch
- wend
- if ch$<>chr$(34) then call er(SYNTAX):sym=undef:exit sub
- nextch
- else
- '...single character
- obj$=ch$
- case
- obj$="+" : sym=plus
- obj$="-" : sym=minus
- obj$="*" : sym=mult
- obj$="/" : sym=div
- obj$="^" : sym=pow
- obj$="(" : sym=lparen
- obj$=")" : sym=rparen
- obj$="=" : sym=eq
- obj$="<" : sym=lt
- obj$=">" : sym=gt
- obj$="," : sym=comma
- obj$=":" : sym=colon
- end case
-
- nextch
-
- '..<= <> >= ?
- if sym=lt and ch$="=" then
- sym=ltoreq:nextch
- else
- if sym=lt and ch$=">" then
- sym=noteq:nextch
- else
- if sym=gt and ch$="=" then
- sym=gtoreq:nextch
- end if
- end if
- end if
-
- if sym=undef then call er(SYNTAX)
- end if
- end if
- end if
-
- 'show.sym(sym)
- END SUB
-
- SUB push(x)
- shared stacktop,stack
-
- if stacktop>maxstack then
- er(STKOVFL)
- else
- stack(stacktop)=x
- ++stacktop
- end if
- END SUB
-
- SUB pop
- shared stacktop,stack
-
- --stacktop
- if stacktop<0 then
- er(STKUFL)
- else
- pop=stack(stacktop)
- end if
- END SUB
-
- SUB func
- shared funcs$,obj$,sym,bad
- longint found
- shortint funct
-
- '..search for the function.
- found=false
- i=1
- while i<=maxfunc and not found
- if funcs$(i) = obj$ then funct=i:found=true else ++i
- wend
-
- if funct then
- '..function
- fun$=funcs$(funct)
- else
- '..variable
- func=0
- exit sub
- end if
-
- '...push the argument
- if funct<8 then
- insymbol
- if sym<>lparen then
- er(SYNTAX)
- funct=0
- else
- insymbol
- expr
- if bad then func=0:exit sub
- if sym<>rparen then call er(SYNTAX):funct=0
- end if
- end if
-
- '...execute function
- case
- funct=1 : push(sin(pop))
- funct=2 : push(cos(pop))
- funct=3 : push(tan(pop))
- funct=4 : push(log(pop))
- funct=5 : push(sqr(pop))
- funct=6 : push(fix(pop))
- funct=7 : push(clng(pop))
- funct=8 : push(rnd)
- end case
-
- func=-1
- END SUB
-
- SUB var_index%(x$)
- var_index% = asc(x$)-asc("A")
- END SUB
-
- SUB factor
- shared sym,obj$,bad,var
-
- if sym=number then
- '..numeric literal
- push(val(obj$))
- else
- '..parenthesised expression?
- if sym=lparen then
- insymbol
- if sym=eos then call er(SYNTAX):exit sub
- expr
- if bad then exit sub
- if sym<>rparen then call er(SYNTAX):exit sub
- else
- '..function or variable?
- if not func then
- if sym=alpha then
- push(var(var_index%(obj$)))
- else
- '..unknown
- er(SYNTAX)
- end if
- end if
- end if
- end if
-
- insymbol
- END SUB
-
- SUB expterm
- shared sym,bad
- factor
- while sym=pow
- insymbol
- factor
- if bad then exit sub
- op2=pop
- op1=pop
- push(op1^op2)
- wend
- END SUB
-
- SUB negterm
- shared sym,bad
- longint negate
- negate=false
- if sym=minus then
- negate=true
- insymbol
- else
- if sym=plus then
- insymbol
- end if
- end if
- expterm
- if bad then exit sub
- if negate then call push(-pop)
- END SUB
-
- SUB term
- shared sym,bad
- shortint op
- negterm
- while sym=mult or sym=div
- op=sym
- insymbol
- negterm
- if bad then exit sub
- op2=pop
- op1=pop
- if op=mult then
- push(op1*op2)
- else
- if op2<>0 then
- push(op1/op2)
- else
- er(DIVBYZERO)
- end if
- end if
- wend
- END SUB
-
- SUB simple_expr
- shared sym,bad
- shortint op
- term
- while sym=plus or sym=minus
- op=sym
- insymbol
- term
- if bad then exit sub
- op2=pop
- op1=pop
- if op=plus then
- push(op1+op2)
- else
- push(op1-op2)
- end if
- wend
- END SUB
-
- SUB expr
- shared sym,bad
- shortint op
- simple_expr
- while sym=eq or sym=lt or sym=gt or sym=ltoreq or sym=gtoreq or sym=noteq
- op=sym
- insymbol
- simple_expr
- if bad then exit sub
- op2=pop
- op1=pop
- case
- op=eq : push(op1=op2)
- op=lt : push(op1<op2)
- op=gt : push(op1>op2)
- op=ltoreq : push(op1<=op2)
- op=gtoreq : push(op1>=op2)
- op=noteq : push(op1<>op2)
- end case
- wend
- END SUB
-
- SUB assign_to_variable
- shared sym,bad,obj$,var
-
- '..variable assignment
- insymbol
- if sym<>alpha then
- er(SYNTAX)
- exit sub
- end if
- variable$=obj$
- insymbol
- if sym=eq then
- insymbol
- if sym=eos then call er(SYNTAX):exit sub
- expr
- if bad then exit sub else var(var_index%(variable$))=pop
- end if
- END SUB
-
- SUB if_statement
- shared sym,bad
-
- '..IF-THEN-ELSE
- insymbol
- expr
- if bad then exit sub
- '..THEN
- if sym=thensym then
- if pop=-1 then
- insymbol
- statement
- while sym<>eos:insymbol:wend
- else
- while sym<>elsesym and sym<>eos
- insymbol
- wend
- '..ELSE (optional)
- if sym=elsesym then
- insymbol
- statement
- end if
- end if
- else
- er(SYNTAX)
- end if
- END SUB
-
- SUB modify_program(num%)
- shared sym,buf$,ut_buf$,code_ptr&
- shared n,length,topline
- longint strptr
-
- { kill or modify a program line }
-
- '..free memory associated with line num%?
- '..(have to do this whether we are
- '...killing OR replacing a line).
-
- if num%<1 or num%>maxlines then call er(LINEOUTOFRANGE):exit sub
-
- strptr=code_ptr&(num%)
- if strptr then
- FreeMem(strptr,len(cstr(strptr))+1&)
- code_ptr&(num%)=NULL
- end if
-
- if n<=length then
- '..** replace line num% if in range **
- if num%>=1 and num%<=maxlines then
- x$=mid$(ut_buf$,n)
- '..check for string literals and don't
- '..change the case of their characters.
- y$=""
- i%=1
- ln%=len(x$)
- while i%<=ln%
- c$=mid$(x$,i%,1)
- if c$=chr$(34) then
- y$=y$+c$
- repeat
- ++i%
- c$=mid$(x$,i%,1)
- if c$<>chr$(34) then y$=y$+c$
- until c$=chr$(34) or i%=ln%
- y$=y$+c$
- ++i%
- else
- y$=y$+ucase$(c$)
- ++i%
- end if
- wend
- x$=y$
-
- '..allocate memory for line and store it.
- strptr=AllocMem(len(x$)+1&,MEMF_PUBLIC or MEMF_CLEAR)
- if strptr=NULL then call er(OUTOFMEMORY):exit sub
- string basic_line address strptr
- basic_line=x$
- code_ptr&(num%)=strptr
- if num%>topline then topline=num%
- else
- er(LINEOUTOFRANGE)
- end if
- else
- '..find next lowest non-null line
- '..after removal of highest line.
- if num%=topline then
- repeat
- --num%
- until code_ptr&(num%)<>NULL or num%<1
- topline=num% '..code_ptr&(0) is sentinel.
- end if
- end if
- END SUB
-
- SUB list_program
- shared code_ptr&,topline
- longint strptr
-
- { list current program }
-
- i%=1
- while i%<=topline
- num$=str$(i%)
- num$=right$(num$,len(num$)-1&)
- strptr=code_ptr&(i%)
- if strptr then print num$;" ";cstr(strptr)
- ++i%
- wend
- END SUB
-
- SUB clear_program
- shared code_ptr&,topline
- longint strptr
-
- { clear program memory }
-
- for i%=0 to maxlines
- strptr=code_ptr&(i%)
- if strptr then
- FreeMem(strptr,len(cstr(strptr))+1&)
- code_ptr&(i%)=NULL
- end if
- next
-
- topline=0
- END SUB
-
- SUB run_program
- shared code_ptr&,pc,old_pc,buf$,ut_buf$
- shared bad,topline,halt_requested
- longint strptr
-
- { execute current program }
-
- if topline<1 then exit sub
-
- pc=1
- repeat
- strptr=code_ptr&(pc)
- old_pc=pc
- ++pc
- if strptr then
- buf$=cstr(strptr)
- ut_buf$=buf$
- parse_line
- end if
- until bad or halt_requested or pc>topline
- END SUB
-
- SUB load_program
- shared sym,obj$,code_ptr&
- shared topline
- longint strptr
-
- { load program from file }
-
- insymbol
- if sym=stringliteral then
- open "I",#2,obj$
- if handle(2)<>NULL then
- clear_program
- print "LOADING ";obj$;".. ";
- while not eof(2)
- input #2,num%
- line input #2,x$
- strptr=AllocMem(len(x$)+1&,MEMF_PUBLIC or MEMF_CLEAR)
- if strptr=NULL then call er(OUTOFMEMORY):close #2:exit sub
- string basic_line address strptr
- basic_line=x$
- code_ptr&(num%)=strptr
- if num%>topline then topline=num%
- wend
- close #2
- print "PROGRAM LOADED."
- else
- er(FILENOTFOUND)
- end if
- else
- er(SYNTAX)
- end if
- END SUB
-
- SUB save_program
- shared sym,obj$,code_ptr&
- shared topline
- longint strptr
-
- { store current program in file }
-
- if topline<1 then exit sub
-
- insymbol
- if sym=stringliteral then
- open "O",#3,obj$
- if handle(3)<>NULL then
- print "SAVING ";obj$;".. ";
- for i%=1 to topline
- strptr=code_ptr&(i%)
- if strptr then print #3,i%;cstr(strptr)
- next
- print "PROGRAM SAVED."
- close #3
- else
- er(CANNOTOPENFILE)
- end if
- else
- er(SYNTAX)
- end if
- END SUB
-
- SUB statement
- shared sym,bad,obj$,var,pc,code_ptr&
- shared halt_requested
-
- '..EMPTY STATEMENT
- if sym=eos then exit sub
-
- '..NUMBER
- if sym=number then
- modify_program(fix(val(obj$)))
- exit sub
- end if
-
- '..CLS
- if sym=clssym then cls:exit sub
-
- '..GOTO
- if sym=gotosym then
- insymbol
- if sym=eos then call er(SYNTAX):exit sub
- expr
- if not bad then pc=pop else exit sub
- if pc<1 or pc>maxlines then call er(LINEOUTOFRANGE)
- if code_ptr&(pc)=NULL then call er(NOSUCHLINE)
- exit sub
- end if
-
- '..IF
- if sym=ifsym then
- if_statement
- exit sub
- end if
-
- '..INPUT
- if sym=inputsym then
- insymbol
- if sym=alpha then
- input var(var_index%(obj$))
- else
- er(SYNTAX)
- end if
- exit sub
- end if
-
- '..LET
- if sym=letsym then
- assign_to_variable
- exit sub
- end if
-
- '..LIST
- if sym=listsym then
- list_program
- exit sub
- end if
-
- '..LOAD
- if sym=loadsym then
- load_program
- exit sub
- end if
-
- '..NEW
- if sym=newsym then
- clear_program
- exit sub
- end if
-
- '..PRINT
- if sym=printsym then
- repeat
- insymbol
- if sym=eos then call er(SYNTAX):exit sub
- if sym=stringliteral then
- print obj$;
- insymbol
- else
- expr
- if not bad then print pop;
- end if
- until sym<>comma
- print
- exit sub
- end if
-
- '..RUN
- if sym=runsym then
- run_program
- exit sub
- end if
-
- '..SAVE
- if sym=savesym then
- save_program
- exit sub
- end if
-
- '..STOP
- if sym=stopsym then
- halt_requested=true
- exit sub '..see run_program
- end if
-
- '..UNKNOWN
- er(SYNTAX)
- END SUB
-
- SUB parse_line
- shared sym,bad,buf$
- shared ch$,n,length,stacktop
- shared halt_requested
- ch$=" "
- n=1
- length=len(buf$)
- bad=false
- halt_requested=false
- stacktop=1
- repeat
- insymbol
- statement
- if sym<>colon and sym<>eos then call insymbol
- until sym<>colon
- END SUB
-
- SUB finished
- shared buf$
-
- '..Quit,Exit?
- if instr(buf$,"QUIT") or instr(buf$,"EXIT") or instr(buf$,"SYSTEM") then
- finished=true
- else
- finished=false
- end if
- END SUB
-
- { ** MAIN ** }
- window 1,"** Tiny BASIC Interpreter © 1993 David Benn **",(0,0)-(640,200)
-
- repeat
- start:
- pc=0
- input ,ut_buf$
- buf$=ucase$(ut_buf$)
- if not finished then call parse_line
- until finished
-
- window close 1
-
- clear_program
- library close exec
-